package com.zlm.custom.spring;

import org.springframework.util.StringUtils;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author zlm
 * @date 2025/4/26
 */
public class ApplicationContext {

    public ApplicationContext(String packageName) throws Exception {
        initContext(packageName);
    }

    private Map<String, Object> ioc = new HashMap<>();

    private Map<String, Object> loadingIoc = new HashMap<>();

    private Map<String,BeanDefinition> beanDefinitionMap = new HashMap<>();

    private List<BeanPostProcessor> postProcessors = new ArrayList<>();


    public void initContext(String packageName) throws Exception {
        //扫描指定包路径，并创建beanDefinition
        scanPackage(packageName).stream().filter(this::scanCreate).forEach(this::wrapper);
        initBeanPostProcessor();
        //初始化bean
        beanDefinitionMap.values().forEach(this::createBean);

        //过滤出添加注解的类
        //List<Class<?>> classList = scanPackage(packageName).stream().filter(c -> c.isAnnotationPresent(Component.class)).collect(Collectors.toList());
        //ApplicationContext.class.getClassLoader().getResource(packageName);

    }

    /**
     * 初始化BeanPostProcessor
     */
    private void initBeanPostProcessor() {
        beanDefinitionMap.values().stream()
                .filter(bd -> BeanPostProcessor.class.isAssignableFrom(bd.getBeanType()))
                .map(this::createBean)
                .map(bean -> (BeanPostProcessor)bean)
                .forEach(postProcessors::add);
    }


    /**
     * 判断是否添加Component注解
     *
     * @param type
     * @return
     */
    protected boolean scanCreate(Class<?> type) {
        return type.isAnnotationPresent(Component.class);
    }

    /**
     * 创建beanDefinition
     *
     * @param type
     * @return
     */
    protected BeanDefinition wrapper(Class<?> type) {
        BeanDefinition beanDefinition = new BeanDefinition(type);
        if(beanDefinitionMap.containsKey(beanDefinition.getName())){
            throw new RuntimeException("bean名字重复");
        }
        beanDefinitionMap.put(beanDefinition.getName(),beanDefinition);
        return beanDefinition;
    }

    /**
     * 创建bean
     *
     * @param beanDefinition
     */
    protected Object createBean(BeanDefinition beanDefinition) {

        String name = beanDefinition.getName();
        if (ioc.containsKey(name)) {
            return ioc.get(name);
        }
        if(loadingIoc.containsKey(name)){
            return loadingIoc.get(name);
        }

        return doCreatedBean(beanDefinition);
    }

    private Object doCreatedBean(BeanDefinition beanDefinition) {
        Constructor<?> constructor = beanDefinition.getConstructor();
        Object bean = null;
        try {
            //创建bean
            bean = constructor.newInstance();
            //放入加载中ioc
            loadingIoc.put(beanDefinition.getName(),bean);
            //注入属性
            autowiredBean(bean,beanDefinition);

            //初始化bean，可以对bean进行修改
            bean = initializeBean(bean,beanDefinition);


            //从加载中ioc中移除
            loadingIoc.remove(beanDefinition.getName());
            //放入ioc中
            ioc.put(beanDefinition.getName(), bean);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return bean;
    }

    private Object initializeBean(Object bean, BeanDefinition beanDefinition) throws Exception {
        //初始化中对bean修改
        for (BeanPostProcessor postProcessor : postProcessors) {
            bean = postProcessor.beforeInitializeBean(bean, beanDefinition.getName());
        }
        //获取添加@PostConstruct的方法
        Method prosConstrcutMethod = beanDefinition.getProsConstrcutMethod();
        if(prosConstrcutMethod != null){
            //执行添加@PostConstrcut的方法
            prosConstrcutMethod.invoke(bean);
        }
        //初始化后对bean修改
        for (BeanPostProcessor postProcessor : postProcessors) {
            bean = postProcessor.afterInitializeBean(bean, beanDefinition.getName());
        }
        return bean;
    }

    private void autowiredBean(Object bean, BeanDefinition beanDefinition) throws IllegalAccessException {
        for (Field autowiredField : beanDefinition.getAutowiredFields()) {
            autowiredField.setAccessible(true);
            Object fieldBean = getBean(autowiredField.getType());
            if(fieldBean == null){
                fieldBean = wrapper(autowiredField.getType());
            }
            autowiredField.set(bean, fieldBean);

        }
    }

    /**
     * 扫描指定包路径
     *
     * @param packageName
     * @return
     */
    private List<Class<?>> scanPackage(String packageName) throws Exception {
        List<Class<?>> classList = new ArrayList<>();
        //得到项目资源路径
        URL resource = this.getClass().getClassLoader().getResource(packageName.replace(".", File.separator));

        assert resource != null;
        Path path = Paths.get(resource.toURI());
        //递归获取路径下文件
        Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                //获取绝对路径
                Path absolutePath = file.toAbsolutePath();
                if (absolutePath.toString().endsWith(".class")) {
                    //System.out.println(absolutePath);
                    String replaceStr = absolutePath.toString().replace(File.separator, ".");
                    int packageIndex = replaceStr.indexOf(packageName);
                    String className = replaceStr.substring(packageIndex, (replaceStr.length() - ".class".length()));
                    try {
                        classList.add(Class.forName(className));
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }
                    //System.out.println(className);

                }

                //无论遇到什么问题，把所有文件递归遍历完
                return FileVisitResult.CONTINUE;
            }
        });

        return classList;
    }

    public Object getBean(String name) {
        if(!StringUtils.hasText(name)){
            return null;
        }
        Object bean = ioc.get(name);
        if(bean != null){
            return bean;
        }
        if(beanDefinitionMap.containsKey(name)){
            return createBean(beanDefinitionMap.get(name));
        }
        return null;
    }

    public <T> T getBean(Class<T> beanType) {
        String beanName = beanDefinitionMap.values().stream()
                .filter(beanDefinition -> beanType.isAssignableFrom(beanDefinition.getBeanType()))
                .map(BeanDefinition::getName)
                .findFirst()
                .orElse(null);
        return (T) getBean(beanName);
        //return ioc.values().stream().
        //        filter(bean -> beanType.isAssignableFrom(bean.getClass()))
        //        .map(bean -> (T) bean)
        //        .findAny()
        //        .orElse(null);
    }

    public <T> List<T> getBeans(Class<T> beanType) {
        return beanDefinitionMap.values().stream()
                .filter(bd -> beanType.isAssignableFrom(bd.getBeanType()))
                .map(BeanDefinition::getName)
                .map(this::getBean)
                .map(bean -> (T) bean)
                .collect(Collectors.toList());

        //return ioc.values().stream().
        //        filter(bean -> beanType.isAssignableFrom(bean.getClass()))
        //        .map(bean -> (T) bean)
        //        .collect(Collectors.toList());
    }

}
